package com.fzu.orderservice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzu.commonutil.entity.*;
import com.fzu.commonutil.mapper.OrderFormMapper;
import com.fzu.commonutil.mapper.ServiceTypeMapper;
import com.fzu.commonutil.params.DataResult;
import com.fzu.commonutil.params.DemandMatchParam;
import com.fzu.commonutil.params.ListParams;
import com.fzu.commonutil.params.SelectResult;
import com.fzu.commonutil.util.*;
import com.fzu.orderservice.client.NormalClient;
import com.fzu.orderservice.params.ConfirmParam;
import com.fzu.orderservice.params.EndParam;
import com.fzu.orderservice.params.DemandToOrderParam;
import com.fzu.orderservice.params.HousekeeperToOrderParam;
import com.fzu.orderservice.result.OrderFormResult;
import com.fzu.orderservice.service.OrderFormService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zyq
 * @since 2023-03-26
 */
@Service
@RequiredArgsConstructor
public class OrderFormServiceImpl extends ServiceImpl<OrderFormMapper, OrderForm> implements OrderFormService {
    private final OrderFormMapper orderFormMapper;
    private final NormalClient normalClient;
    private final RedisUtil redisUtil;
    private final ServiceTypeMapper serviceTypeMapper;

    @Override
    public PageVO page(OrderForm orderForm, User user, Integer pageNum, Integer pageSize) {
        // 构造查询条件
        LambdaQueryWrapper<OrderForm> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(OrderForm::getId,
                OrderForm::getBeginTime,
                OrderForm::getEndTime,
                OrderForm::getMoney,
                OrderForm::getCreateTime,
                OrderForm::getHousekeeperId,
                OrderForm::getCompanyId,
                OrderForm::getOrderNo,
                OrderForm::getName,
                OrderForm::getCity,
                OrderForm::getProvince,
                OrderForm::getTown,
                OrderForm::getDetail,
                OrderForm::getState,
                OrderForm::getPay,
                OrderForm::getServiceCount);
        Integer role = user.getRole();
        Integer userId = user.getId();
        Integer companyId = user.getCompanyId();
        // 用于标识是否有查询条件
        boolean paramTag = false;
        // 客户只能查看自己的订单
        if (CodeEnum.CONSUMER.getCode().equals(role)) {
            wrapper.eq(OrderForm::getUserId, userId);
            paramTag = true;
        }
        // 公司只能查看自己接的订单
        if (CodeEnum.COMPANY.getCode().equals(role)) {
            wrapper.eq(OrderForm::getCompanyId, companyId);
            paramTag = true;
        }

        String orderNo = orderForm.getOrderNo();
        if (StringUtils.isNotBlank(orderNo)) {
            wrapper.eq(OrderForm::getOrderNo, orderNo);
            paramTag = true;
        }
        Integer state = orderForm.getState();
        if (state != null && !new Integer(-1).equals(state)) {
            wrapper.eq(OrderForm::getState, state);
            paramTag = true;
        }
        String name = orderForm.getName();
        if (StringUtils.isNotBlank(name)) {
            wrapper.eq(OrderForm::getName, name);
            paramTag = true;
        }
        String phone = orderForm.getPhone();
        if (StringUtils.isNotBlank(phone)) {
            wrapper.eq(OrderForm::getPhone, phone);
            paramTag = true;
        }
        Integer company = orderForm.getCompanyId();
        if (company != null) {
            wrapper.eq(OrderForm::getCompanyId, company);
            paramTag = true;
        }
        wrapper.orderByDesc(OrderForm::getCreateTime);

        // 当无查询条件时，从redis获取订单总数，加快查询速度
        int total = paramTag ? 0: (Integer) redisUtil.get(RedisKey.ORDER_COUNT);
        // 构造分页参数，total不为0时不执行select count语句
        Page<OrderForm> pageRequest = new Page<>(pageNum, pageSize, total);
        orderFormMapper.selectPage(pageRequest, wrapper);
        List<OrderForm> records = pageRequest.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            Set<Integer> idList = records.stream().filter(e -> e.getCompanyId() != null).map(OrderForm::getCompanyId).collect(Collectors.toSet());
            if (!CollectionUtils.isEmpty(idList)) {
                Map<Integer, String> companyMap = normalClient.companyMap(new ListParams().setIdList(idList));
                for (OrderForm record : records) {
                    Integer recordCompanyId = record.getCompanyId();
                    record.setCompanyName(companyMap.get(recordCompanyId));
                    R r = normalClient.housekeeperDetail(record.getHousekeeperId());
                    record.setHousekeeper((Housekeeper)R.getDataFromR(r, Housekeeper.class));
                }
            }
        }
        return PageVO.convert(pageRequest);
    }

    @Override
    public OrderFormResult detail(Integer id) {
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }

        OrderFormResult result = new OrderFormResult();
        BeanUtils.copyProperties(orderForm, result);
        Integer housekeeperId = orderForm.getHousekeeperId();
        if (housekeeperId != null) {
            R r = normalClient.housekeeperDetail(housekeeperId);
            Housekeeper housekeeper = (Housekeeper) R.getDataFromR(r, Housekeeper.class);
            result.setHousekeeper(housekeeper);
        }
        Integer companyId = orderForm.getCompanyId();
        if (companyId != null) {
            R r = normalClient.companyDetail(companyId);
            Company company = (Company) R.getDataFromR(r, Company.class);
            result.setCompany(company);
        }
        Integer serviceTypeId = orderForm.getServiceTypeId();
        if (serviceTypeId != null) {
            R r = normalClient.serviceTypeDetail(serviceTypeId);
            ServiceType serviceType = (ServiceType) R.getDataFromR(r, ServiceType.class);
            if (serviceType != null) {
                result.setServiceTypeName(serviceType.getName());
            }
        }
        return result;
    }

    @Override
//    @GlobalTransactional
    public void demandToOrder(DemandToOrderParam param, User user) {
        Integer demandId = param.getDemandId();
//        throw new ApiException(ErrorEnum.CHECK_ERROR);
        orderFormMapper.insert(new OrderForm());
        // 查询需求信息
        R r1 = normalClient.demandInfo(demandId);
        Demand demand = (Demand) R.getDataFromR(r1, Demand.class);
        // 查询家政员信息
        Integer housekeeperId = param.getHousekeeperId();
        R r2 = normalClient.housekeeperInfo(housekeeperId);
        Housekeeper housekeeper = (Housekeeper) R.getDataFromR(r2, Housekeeper.class);
        OrderForm orderForm = new OrderForm();
        orderForm.setUserId(user.getId())
                .setAccommodation(demand.getAccommodation())
                .setBeginTime(param.getBeginTime())
                .setEndTime(param.getEndTime())
                .setChargeMode(demand.getChargeMode())
                .setName(demand.getName())
                .setPhone(demand.getPhone())
                .setProvince(demand.getProvince())
                .setCity(demand.getCity())
                .setTown(demand.getTown())
                .setDetail(demand.getDetail())
                .setPay(param.getPay())
                .setServiceCount(param.getServiceCount())
                .setCompanyId(housekeeper.getCompanyId())
                .setHousekeeperId(housekeeperId)
                .setMoney(param.getServiceCount() * param.getPay())
                .setRemark(param.getRemark())
                .setServiceTypeId(demand.getServiceTypeId());

        // 更新需求匹配状态
        DemandMatchParam p = new DemandMatchParam();
        p.setDemandId(demandId);
        p.setHousekeeperId(param.getHousekeeperId());
        p.setCompanyId(housekeeper.getCompanyId());
        R r3 = normalClient.match(p);
        if (!r3.getCode().equals(200)) {
            System.out.println("请求错误");
            throw new ApiException(r3.getCode(), r3.getMessage());
        }
        // 创建订单
        this.createOrder(orderForm);
    }

    @Override
//    @GlobalTransactional
    public void housekeeperToOrder(HousekeeperToOrderParam param, User user) {
        // 查询地址簿信息
        Integer addressId = param.getAddressId();
        R r1 = normalClient.addressBookInfo(addressId);
        AddressBook addressBook = (AddressBook) R.getDataFromR(r1, AddressBook.class);
//        AddressBook addressBook = param.getAddressBook();
        // 查询家政员信息
        Integer housekeeperId = param.getHousekeeperId();
        R r2 = normalClient.housekeeperInfo(housekeeperId);
        Housekeeper housekeeper = (Housekeeper) R.getDataFromR(r2, Housekeeper.class);
        OrderForm orderForm = new OrderForm();
        orderForm.setUserId(user.getId())
                .setAccommodation(param.getAccommodation())
                .setBeginTime(param.getBeginTime())
                .setEndTime(param.getEndTime())
                .setChargeMode(param.getChargeMode())
                .setName(addressBook.getName())
                .setPhone(addressBook.getPhone())
                .setProvince(addressBook.getProvince())
                .setCity(addressBook.getCity())
                .setTown(addressBook.getTown())
                .setDetail(addressBook.getDetail())
                .setPay(param.getPay())
                .setServiceCount(param.getServiceCount())
                .setCompanyId(housekeeper.getCompanyId())
                .setHousekeeperId(param.getHousekeeperId())
                .setMoney(param.getServiceCount() * param.getPay())
                .setRemark(param.getRemark())
                .setServiceTypeId(param.getServiceTypeId());
        // 创建订单
        this.createOrder(orderForm);
    }

    @Override
    public void cancel(Integer id) {
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, state);
        if (OrderStateEnum.WAIT_ACCEPT.getCode().equals(state)) {
            updateWrapper.set(OrderForm::getState, OrderStateEnum.WAIT_ACCEPT_CANCEL.getCode());
        } else if (OrderStateEnum.WAIT_PAY.getCode().equals(state)) {
            updateWrapper.set(OrderForm::getState, OrderStateEnum.WAIT_PAY_CANCEL.getCode());
        } else if (OrderStateEnum.WAIT_START.getCode().equals(state)) {
            updateWrapper.set(OrderForm::getState, OrderStateEnum.WAIT_START.getCode());
        } else {
            throw new ApiException(ErrorEnum.STATE_ERROR);
        }
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    public void accept(Integer id, User user) {
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        if (!orderForm.getCompanyId().equals(user.getCompanyId()) || !OrderStateEnum.WAIT_ACCEPT.getCode().equals(state)) {
            throw new ApiException(ErrorEnum.MESSAGE_ERROR);
        }
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, OrderStateEnum.WAIT_ACCEPT.getCode())
                .set(OrderForm::getState, OrderStateEnum.WAIT_PAY.getCode());
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    public void pay(Integer id, User user) {
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        if (!orderForm.getUserId().equals(user.getId()) || !OrderStateEnum.WAIT_PAY.getCode().equals(state)) {
            throw new ApiException(ErrorEnum.MESSAGE_ERROR);
        }
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, OrderStateEnum.WAIT_PAY.getCode())
                .set(OrderForm::getState, OrderStateEnum.WAIT_START.getCode());
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    public void start(Integer id, User user) {
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        if (!orderForm.getUserId().equals(user.getId()) || !OrderStateEnum.WAIT_START.getCode().equals(state)) {
            throw new ApiException(ErrorEnum.STATE_ERROR);
        }
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, OrderStateEnum.WAIT_START.getCode())
                .set(OrderForm::getActualBeginTime, new Date())
                .set(OrderForm::getState, OrderStateEnum.PROCESSING.getCode());
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    public void end(EndParam param, User user) {
        Integer id = param.getId();
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        if (!orderForm.getUserId().equals(user.getId()) || !OrderStateEnum.PROCESSING.getCode().equals(state)) {
            throw new ApiException(ErrorEnum.MESSAGE_ERROR);
        }
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, OrderStateEnum.PROCESSING.getCode())
                .set(OrderForm::getActualServiceCount, param.getServiceCount())
                .set(OrderForm::getScore, param.getScore())
                .set(OrderForm::getCommentContent, param.getContent())
                .set(OrderForm::getActualMoney, orderForm.getPay() * param.getServiceCount())
                .set(OrderForm::getActualEndTime, new Date())
                .set(OrderForm::getState, OrderStateEnum.WAIT_CONFIRM.getCode());
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    public void confirm(ConfirmParam param, User user) {
        Integer id = param.getId();
        OrderForm orderForm = orderFormMapper.selectById(id);
        if (orderForm == null) {
            throw new ApiException(ErrorEnum.OBJECT_NOT_FOUND);
        }
        Integer state = orderForm.getState();
        if (!orderForm.getCompanyId().equals(user.getCompanyId()) || !OrderStateEnum.WAIT_CONFIRM.getCode().equals(state)) {
            throw new ApiException(ErrorEnum.MESSAGE_ERROR);
        }
        Integer serviceCount = param.getServiceCount();
        Double pay = orderForm.getPay();
        double money = serviceCount * pay;
        LambdaUpdateWrapper<OrderForm> updateWrapper = new UpdateWrapper<OrderForm>().lambda();
        updateWrapper.eq(OrderForm::getId, id)
                .eq(OrderForm::getState, OrderStateEnum.WAIT_CONFIRM.getCode())
                .set(OrderForm::getState, OrderStateEnum.FINISHED.getCode())
                .set(OrderForm::getServiceCount, serviceCount)
                .set(OrderForm::getMoney, money);
        int update = orderFormMapper.update(orderForm, updateWrapper);
        if (update != 1) {
            throw new ApiException(ErrorEnum.UPDATE_ERROR);
        }
    }

    @Override
    @Async("normal-thread-pool")
    public CompletableFuture<DataResult> asyncTask1(Integer companyId) {
        DataResult dataResult = new DataResult();
        System.out.println("异步查询任务2：" + Thread.currentThread().getName());

        DateFormat df = new SimpleDateFormat("yyyy-MM");
        Calendar calendar = Calendar.getInstance();
        LinkedList<String> month = new LinkedList<>();
        LinkedList<Integer> count = new LinkedList<>();
        for (int i = 0; i < 6; i++) {
            Date date = calendar.getTime();
            Date begin = DateUtils.firstDateOfMonth(date);
            Date end = DateUtils.lastDateOfMonth(date);
            count.addFirst(orderFormMapper.betweenCount(companyId, begin, end));
            month.addFirst(df.format(date));
            calendar.add(Calendar.MONTH, -1);
        }
        dataResult.setOrderData(count);
        dataResult.setOrderLabel(month);
        dataResult.setCompanyId(companyId);
        return CompletableFuture.completedFuture(dataResult);
    }

    @Override
    @Async("normal-thread-pool")
    public CompletableFuture<DataResult> asyncTask2(Integer companyId, Integer roleId) {
        DataResult dataResult = new DataResult();
        System.out.println("异步查询任务2：" + Thread.currentThread().getName());
        List<ServiceType> serviceTypes = normalClient.serviceTypeAll();
        Calendar instance = Calendar.getInstance();
        Date end = instance.getTime();
        instance.set(Calendar.MONTH, 0);
        instance.set(Calendar.DAY_OF_MONTH, 1);
        Date begin = instance.getTime();
        List<SelectResult> typeCountResult = orderFormMapper.curYearTypeCount(companyId, begin, end);
        Map<Integer, Integer> typeCountMap = new HashMap<>(0);
        if (!CollectionUtils.isEmpty(typeCountResult)) {
            typeCountMap = typeCountResult.stream().collect(Collectors.toMap(SelectResult::getServiceTypeId, SelectResult::getTotal));
        }
        List<String> typeLabel = new ArrayList<>(serviceTypes.size());
        List<Integer> typeData = new ArrayList<>(serviceTypes.size());
        for (ServiceType type : serviceTypes) {
            typeLabel.add(type.getName());
            Integer c = typeCountMap.get(type.getId());
            if (c == null) {
                c = 0;
            }
            typeData.add(c);
        }
        dataResult.setTypeLabel(typeLabel);
        dataResult.setTypeData(typeData);
        dataResult.setCompanyId(companyId);
        LambdaQueryWrapper<OrderForm> wrapper = new LambdaQueryWrapper<>();
        if (CodeEnum.COMPANY.getCode().equals(roleId)) {
            wrapper.eq(OrderForm::getCompanyId, companyId);
        }
        dataResult.setOrderCount(orderFormMapper.selectCount(wrapper));
        return CompletableFuture.completedFuture(dataResult);
    }


    /**
     * 创建订单
     *
     * @param orderForm
     */
    private void createOrder(OrderForm orderForm) {
        // 分布式锁
        String key = RedisKey.CREATE_ORDER_HOUSEKEEPER + orderForm.getHousekeeperId();
//        boolean lock = redisUtil.setNx(key);
        try {
//            if (!lock) {
//                throw new ApiException(ErrorEnum.BUSY);
//            }
            redisUtil.expire(key, 2 * 60 * 1000);
            orderForm.setOrderNo(KeyUtil.generateOrderNo())
                    .setState(OrderStateEnum.WAIT_ACCEPT.getCode());
            // 检查家政员在同时间段内有订单
            LambdaQueryWrapper<OrderForm> wrapper = new LambdaQueryWrapper<>();
            // 相同家政员 订单有效 服务时间重叠
            wrapper.eq(OrderForm::getHousekeeperId, orderForm.getHousekeeperId())
                    .in(OrderForm::getState,
                            OrderStateEnum.WAIT_ACCEPT.getCode(),
                            OrderStateEnum.WAIT_PAY.getCode(),
                            OrderStateEnum.WAIT_START.getCode(),
                            OrderStateEnum.PROCESSING.getCode())
                    .ge(OrderForm::getEndTime, orderForm.getBeginTime());
            Integer sameTimeCount = orderFormMapper.selectCount(wrapper);
            if (sameTimeCount > 0) {
                throw new ApiException("该家政员同时段内已分配任务，请更换家政员或咨询家政公司！");
            }
            // 创建订单
            orderFormMapper.insert(orderForm);
            // 订单数增加
            redisUtil.incr(RedisKey.ORDER_COUNT, 1);
        } finally {
            // 分布式锁解除
//            redisUtil.del(key);
        }
    }
}
